/*
 * Licensed Materials - Property of tenxcloud.com
 * (C) Copyright 2022 TenxCloud. All Rights Reserved.
 * 2022-11-13  @author song
 */
package oidc

import (
	"context"
	"crypto/tls"
	"net/http"
	"os"

	oidc "github.com/coreos/go-oidc"
)

type oidcConfig struct {
	IssuerURL     string
	UsernameClaim string
	GroupsClaim   string
	ClientID      string
	Claims        Identity
}

// Identity represents the ID Token claims supported by the server.
type Identity struct {
	UserID            string `json:"userid"`
	Username          string `json:"name"`
	PreferredUsername string `json:"preferred_username"`
	Email             string `json:"email"`
	EmailVerified     bool   `json:"email_verified"`

	Groups []string `json:"groups"`

	// ConnectorData holds data used by the connector for subsequent requests after initial
	// authentication, such as access tokens for upstream provides.
	//
	// This data is never shared with end users, OAuth clients, or through the API.
	ConnectorData []byte
}

type loginInfo interface {
	GetUserID() int
	GetUserName() string
	GetUserToken() string
}

func (i Identity) GetUserID() int {
	return 0
}

func (i Identity) GetUserName() string {
	return i.PreferredUsername
}
func (i Identity) GetUserToken() string {
	return ""
}

func NewOIDCAuthorizer() *oidcConfig {
	o := &oidcConfig{}
	o.setDefault()
	o.SetFromEnv()
	return o
}

func (o *oidcConfig) setDefault() {
	o.GroupsClaim = "groups"
	o.UsernameClaim = "preferred_username"
}

func (o *oidcConfig) SetFromEnv() {
	if issuerURL := os.Getenv("OIDC_SERVER_URL"); issuerURL != "" {
		o.IssuerURL = issuerURL
	}
	if clientID := os.Getenv("OIDC_CLIENT_ID"); clientID != "" {
		o.ClientID = clientID
	}

}

// Validating is used to validate idToken.
func (o *oidcConfig) Validate(token string) (loginInfo, error) {
	ctx := oidc.ClientContext(context.TODO(), &http.Client{
		Transport: &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}})
	provider, err := oidc.NewProvider(ctx, o.IssuerURL)
	if err != nil {
		return nil, err
	}
	verifier := provider.Verifier(&oidc.Config{ClientID: o.ClientID})
	idToken, err := verifier.Verify(context.TODO(), token)
	if err != nil {
		return nil, err
	}
	if err := idToken.Claims(&o.Claims); err != nil {
		return nil, err
	}
	return o.Claims, nil
}
